#include "AudioDecode.h"
#include "FmtCvt.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define AUDIO_REFILL_THRESH 4096

AudioDecode::AudioDecode():
    m_codec(NULL),
    m_codec_ctx(NULL),
    m_parser_ctx(NULL),
    m_pkt(NULL),
    m_frm(NULL),
    m_swr(NULL),
    m_buffer(NULL),
    m_buffer_size(0),
    m_buffer_capacity(32000)
{
    m_swr = new FmtCvt();
    m_buffer = (uint8_t*)malloc(m_buffer_capacity);
    //av_register_all();
}

AudioDecode::~AudioDecode()
{
    destroy();
    delete m_swr;
    if(NULL!=m_buffer)
    {
        free(m_buffer);
        m_buffer = NULL;
    }
}

int AudioDecode::init(enum AVCodecID id)
{
    if(m_codec)
    {
        return -1;
    }
    /* 查找mp3解码器 */
    m_codec = avcodec_find_decoder(id);
    if (!m_codec) {
        fprintf(stderr, "Codec not found\n");
        //exit(1);
        return -2;
    }

    m_codec_ctx = avcodec_alloc_context3(m_codec);
    if (!m_codec_ctx) {
        destroy();
        fprintf(stderr, "Could not allocate audio codec context\n");
        //exit(1);
        return -3;
    }

    /* 打开解码器*/
    if (avcodec_open2(m_codec_ctx, m_codec, NULL) < 0) {
        destroy();
        fprintf(stderr, "Could not open codec\n");
        //exit(1);
        return -4;
    }

    m_parser_ctx = av_parser_init(m_codec->id);
    if (!m_parser_ctx) {
        destroy();
        fprintf(stderr, "Parser not found\n");
        //exit(1);
        return -5;
    }

    if (!(m_pkt = av_packet_alloc())) {
        destroy();
        fprintf(stderr, "Could not allocate audio packet\n");
        //exit(1);
        return -6;
    }

    if (!(m_frm = av_frame_alloc())) {
        destroy();
        fprintf(stderr, "Could not allocate audio frame\n");
        //exit(1);
        return -7;
    }

    return 0;
}

int AudioDecode::destroy()
{
    if(m_codec)
    {
        m_codec = NULL;
    }
    if(m_codec_ctx)
    {
        avcodec_free_context(&m_codec_ctx);
        m_codec_ctx = NULL;
    }
    if(m_parser_ctx)
    {
        av_parser_close(m_parser_ctx);
        m_parser_ctx = NULL;
    }
    if(m_frm)
    {
        av_frame_free(&m_frm);
        m_frm = NULL;
    }
    if(m_pkt)
    {
        av_packet_free(&m_pkt);
        m_pkt = NULL;
    }
    return 0;
}

int AudioDecode::decode(const uint8_t* data, int data_size, uint8_t** out_pptr)
{
    if(!m_codec)
    {
        return -1;
    }

    //if((m_buffer_left_size+data_size) > m_buffer_size)
    //{
        //m_buffer_size = m_buffer_left_size+data_size;
        //m_buffer = (uint8_t*)realloc(m_buffer, m_buffer_size);
        //memcpy(m_buffer+m_buffer_left_size, data, data_size);
        //m_buffer_left_size += data_size;
    //}

    //if (data_size < AUDIO_REFILL_THRESH) {
        ////exit(1);
    //}

    m_buffer_size = 0;
    *out_pptr = m_buffer;

    while (data_size > 0) {
        //printf("in size = %d\n", data_size);
        int ret = av_parser_parse2(m_parser_ctx, m_codec_ctx, &m_pkt->data, &m_pkt->size,
                data, data_size,
                AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
        if (ret < 0) {
            fprintf(stderr, "Error while parsing\n");
            exit(1);
        }
        if (m_pkt->size)
        {
            _decode(m_codec_ctx, m_pkt, m_frm);
        }
        data      += ret;
        data_size -= ret;
        //printf("out size = %d\n", ret);
    }

    return m_buffer_size;
}

//FILE *outfile
void AudioDecode::_decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame)
{
    /* 发送编码数据包给解码器 */
    int ret = avcodec_send_packet(dec_ctx, pkt);
    //if (ret < 0) {
    if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF){
        fprintf(stderr, "Error submitting the packet to the decoder\n");
        return;
    }

    //printfFormat(dec_ctx);

    //static FILE* outfile = NULL;
    //if(0==outfile)
    //{
        //outfile = fopen("out.pcm", "wb");
    //}

    /////////////////////////////////////////
    /* 读取所有的输出帧*/
    //while (ret >= 0) {
    ret = avcodec_receive_frame(dec_ctx, frame);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        return;
    else if (ret < 0) {
        fprintf(stderr, "Error during decoding\n");
        exit(1);
    }
    int data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
    if (data_size < 0) {
        /* 不应该出现的情况 */
        fprintf(stderr, "Failed to calculate data size\n");
        exit(1);
    }
    //for (int i = 0; i < frame->nb_samples; i++)
        //for (int ch = 0; ch < dec_ctx->channels; ch++)
            //fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile);
    //}
    /////////////////////////////////////////

    if (frame->channels > 0 && frame->channel_layout == 0)
        frame->channel_layout = av_get_default_channel_layout(frame->channels);
    else if (frame->channels == 0 && frame->channel_layout > 0)
        frame->channels = av_get_channel_layout_nb_channels(frame->channel_layout);

    //printf("channel_num : %d\n", av_get_channel_layout_nb_channels(frame->channel_layout));
    //printf("sample_fmt : %s\n", av_get_sample_fmt_name((AVSampleFormat)frame->format));
    //printf("bytes_per_sample : %d\n", av_get_bytes_per_sample((AVSampleFormat)frame->format));
    //printf("sample_rate : %d\n", frame->sample_rate);

    uint8_t* out_buffer;
    m_swr->init(dec_ctx);
    int size = m_swr->convert(frame, &out_buffer);
    if(size>0)
    {
        if((m_buffer_size+size) > m_buffer_capacity)
        {
            m_buffer_capacity = m_buffer_size+size;
            m_buffer = (uint8_t*)realloc(m_buffer, m_buffer_capacity);
        }
        memcpy(m_buffer+m_buffer_size, out_buffer, size);
        m_buffer_size += size;
    }

    //static FILE* outdec = NULL;
    //if(0==outdec)
    //{
        //outdec = fopen("outdec.pcm", "wb");
    //}
    //if(0!=outdec)
    //{
        //fwrite(out_buffer, 1, size, outdec);
    //}
}


